/**
 * Software License, Version 1.0 Copyright 2003 The Trustees of Indiana
 * University. All rights reserved. Redistribution and use in source and binary
 * forms, with or without modification, are permitted provided that the
 * following conditions are met: 1) All redistributions of source code must
 * retain the above copyright notice, the list of authors in the original source
 * code, this list of conditions and the disclaimer listed in this license; 2)
 * All redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the disclaimer listed in this license in the
 * documentation and/or other materials provided with the distribution; 3) Any
 * documentation included with all redistributions must include the following
 * acknowledgement: "This product includes software developed by the Community
 * Grids Lab. For further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/." Alternatively, this acknowledgement may
 * appear in the software itself, and wherever such third-party acknowledgments
 * normally appear. 4) The name Indiana University or Community Grids Lab or
 * NaradaBrokering, shall not be used to endorse or promote products derived
 * from this software without prior written permission from Indiana University.
 * For written permission, please contact the Advanced Research and Technology
 * Institute ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202. 5)
 * Products derived from this software may not be called NaradaBrokering, nor
 * may Indiana University or Community Grids Lab or NaradaBrokering appear in
 * their name, without prior written permission of ARTI. Indiana University
 * provides no reassurances that the source code provided does not infringe the
 * patent or any other intellectual property rights of any other entity. Indiana
 * University disclaims any liability to any recipient for claims brought by any
 * other entity based on infringement of intellectual property rights or
 * otherwise. LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH
 * NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY
 * GIVES NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 * GENERATED USING SOFTWARE.
 */
/**
 * Software License, Version 1.0 Copyright 2003 The Trustees of Indiana
 * University. All rights reserved. Redistribution and use in source and binary
 * forms, with or without modification, are permitted provided that the
 * following conditions are met: 1) All redistributions of source code must
 * retain the above copyright notice, the list of authors in the original source
 * code, this list of conditions and the disclaimer listed in this license; 2)
 * All redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the disclaimer listed in this license in the
 * documentation and/or other materials provided with the distribution; 3) Any
 * documentation included with all redistributions must include the following
 * acknowledgement: "This product includes software developed by the Community
 * Grids Lab. For further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/." Alternatively, this acknowledgement may
 * appear in the software itself, and wherever such third-party acknowledgments
 * normally appear. 4) The name Indiana University or Community Grids Lab or
 * NaradaBrokering, shall not be used to endorse or promote products derived
 * from this software without prior written permission from Indiana University.
 * For written permission, please contact the Advanced Research and Technology
 * Institute ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202. 5)
 * Products derived from this software may not be called NaradaBrokering, nor
 * may Indiana University or Community Grids Lab or NaradaBrokering appear in
 * their name, without prior written permission of ARTI. Indiana University
 * provides no reassurances that the source code provided does not infringe the
 * patent or any other intellectual property rights of any other entity. Indiana
 * University disclaims any liability to any recipient for claims brought by any
 * other entity based on infringement of intellectual property rights or
 * otherwise. LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH
 * NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY
 * GIVES NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 * GENERATED USING SOFTWARE.
 */
package cgl.narada.transport.ssl;

import java.io.File;

import org.apache.log4j.Logger;
import org.doomdark.uuid.UUIDGenerator;

/**
 * @author Hongbin Liu
 * @author Harshawardhan Gadgil
 * @version 1.0
 */

public class SSLJSSEControl {
	final static Logger log = Logger.getLogger(SSLJSSEControl.class.getName());

	public static final SSLJSSEControl INSTANCE = new SSLJSSEControl();

	public static final String KEY_SUFFIX = ".key";
	public static final String CER_SUFFIX = ".cer";
	public static final String TEMP_NAME = "serverCerfiticate.cer";
	public static final String PARENT_DIR = "sslkeys";

	// HG: alias for the generated certificate
	private static final String alias = "sslCert";

	protected String trustStoreFile;
	private String keyStorePass = "passpass";
	public String trustStorePass = "truststorepass";
	private String uniqueId;
	private String dir;
	private String keyStoreFile;
	private String serverCertificate;

	// private String removeCmd;

	/*
	 * A lesson learned. I used an instance variable "initialized". inside
	 * #public# constructor, conditionally calling set methods only if not
	 * initialized. This NOT WORK!! Because new instance will be created with
	 * its instance fields uninitialized (some of them are null)
	 */
	private SSLJSSEControl() {
		/* They are order sensitive */
		setUniqueId();
		setFileNames();
	}

	// private void setRemoveCmd() {
	// String os = System.getProperty("os.name");
	// if (os.indexOf("Windows") > 0 || os.indexOf("Win") > 0)
	// removeCmd = "del";
	// else
	// removeCmd = "rm";
	// }

	private void setUniqueId() {
		// try {
		// uniqueId = InetAddress.getLocalHost().getHostName();
		// }
		// catch (UnknownHostException uhe) {
		// log.warn("missing uniqueId: unknown localhost");
		// uhe.printStackTrace();
		// }

		// Use the 128-bit UUID generator to create a unique time based string
		uniqueId = UUIDGenerator.getInstance().generateTimeBasedUUID()
				.toString();
	}

	private void setControlDir() {
		/*
		 * We can assume NARADA_HOME unique across the given system? user.home
		 * would be problematic since two NB processes on the same system,
		 * holding different certificates, will have the same uniqueId.
		 */
		dir = System.getProperty("user.home");
		// if (dir == null) dir = System.getProperty("NB_HOME");
		String fsep = System.getProperty("file.separator");

		// dir = System.getProperty("java.home") + fsep + "lib" + fsep
		// + "security";

		// assert dir != null;

		if (!dir.endsWith(fsep)) dir = dir + fsep;
		dir = dir + PARENT_DIR + fsep;
	}

	private void setFileNames() {
		setControlDir();
		new File(dir).mkdirs();

		// HG: File suffixes changed to meaning ful names

		// HG: Modified March 21, 2006:
		// If the Keystore and keystore passord are already set then no need to
		// do this...
		
		System.out.println(System.getProperty("javax.net.ssl.trustStore"));
		if (System.getProperty("javax.net.ssl.trustStore") != null) {
			keyStoreFile = System.getProperty("javax.net.ssl.keyStore");
			keyStorePass = System.getProperty("javax.net.ssl.keyStorePassword");
			trustStoreFile = System.getProperty("javax.net.ssl.truststore");
			trustStorePass = System
					.getProperty("javax.net.ssl.trustStorePassword");
		}
		else {

			keyStoreFile = dir + uniqueId + ".KEYSTORE";
			trustStoreFile = dir + uniqueId + ".TRUSTSTORE";
			serverCertificate = dir + TEMP_NAME;
			log.debug("set tempFile name " + serverCertificate);
		}

		log.debug("set keystore file name " + keyStoreFile);
		log.debug("set trustStore file name " + trustStoreFile);
	}

	public static SSLJSSEControl getInstance() {
		return INSTANCE;
	}

	public String getControlDir() {
		return dir;
	}

	public String trustStoreFile() {
		return trustStoreFile;
	}

	public String keyStoreFile() {
		return keyStoreFile;
	}

	public String keyStorePass() {
		return keyStorePass;
	}

	public String trustStorePass() {
		return trustStorePass;
	}

	/**
	 * @return a String to uniquely identify SSL key/trust store file for given
	 *         NB process
	 */
	public String uniqueId(String host, Object obj) {
		/*
		 * If there are more than one NB processes and they use different
		 * keystore, then we'll have to find another way to generate uniqueId
		 */
		return uniqueId;
	}

	public String reGenKeys() throws SSLControlException {
		if (new File(keyStoreFile).exists()) {
			new File(keyStoreFile).delete();
			log.debug("old keystore deleted: " + keyStoreFile);
		}
		return execGenKeys();
	}

	public String genKeys() throws SSLControlException {
		if (new File(keyStoreFile).exists()) {
			log.debug("old keystore exists " + keyStoreFile);
			return keyStoreFile;
		}
		else
			return execGenKeys();
	}

	private String execGenKeys() throws SSLControlException {
		/** just in case * */
		if (new File(keyStoreFile).exists()) {
			new File(keyStoreFile).delete();
			log.debug("old keystore deleted: " + keyStoreFile);
		}

		/** how to deal with keystore pass? * */
		String[] genkey = {
				"keytool",
				"-genkey",
				"-keystore",
				"\"" + keyStoreFile + "\"",
				"-alias",
				alias,
				"-storetype",
				"JKS",
				"-keyalg",
				"rsa",
				"-dname",
				"\"CN=Name, OU=Organizational Unit, O=Organization, L=City, S=State, C=Country\"",
				"-storepass", keyStorePass, "-keypass", keyStorePass
		};

		// System.out.println("\n-------------Executing: ");
		// for (int x = 0; x < genkey.length; x++) {
		// System.out.print(genkey[x] + " ");
		// }
		// System.out.println("\n--------------");

		Runtime rt = Runtime.getRuntime();
		try {
			Process proc = rt.exec(genkey);
			proc.waitFor();
			log.debug("private/public key pair generated");
		}
		catch (Throwable t) {
			log.warn("unable to generate private/public key pair");
			throw new SSLControlException(t.getMessage(), t);
		}

		// /** how to deal with keystore pass? * */
		// String[] genkey2 = {
		// "keytool",
		// "-selfcert",
		// "-keystore",
		// "\"" + keyStoreFile + "\"",
		// "-alias",
		// alias,
		// "-storetype",
		// "JKS",
		// "-keyalg",
		// "rsa",
		// "-dname",
		// "\"CN=Name, OU=Organizational Unit, O=Organization, L=City, S=State,
		// C=Country\"",
		// "-storepass", keyStorePass, "-keypass", keyStorePass
		// };
		//
		// System.out.println("\n-------------Executing: ");
		// for (int x = 0; x < genkey2.length; x++) {
		// System.out.print(genkey2[x] + " ");
		// }
		// System.out.println("\n--------------");
		//
		// Runtime rt2 = Runtime.getRuntime();
		// try {
		// Process proc2 = rt2.exec(genkey2);
		// proc2.waitFor();
		// log.debug("private/public key pair generated");
		// }
		// catch (Throwable t) {
		// log.warn("unable to generate private/public key pair");
		// throw new SSLControlException(t.getMessage(), t);
		// }

		return keyStoreFile;
	}

	public String reGenCert() throws SSLControlException {
		if (new File(trustStoreFile).exists()) {
			new File(trustStoreFile).delete();
			log.debug("old truststore file deleted: " + trustStoreFile);
		}
		return execGenCert();
	}

	public String genCert() throws SSLControlException {
		if (new File(trustStoreFile).exists()) {
			log.debug("old truststore file exists: " + trustStoreFile);
			return trustStoreFile;
		}
		else
			return execGenCert();
	}

	private String execGenCert() throws SSLControlException {

		if (!new File(keyStoreFile).exists()) {
			log
					.warn("unable to import certificate, no keystore"
							+ keyStoreFile);
			throw new SSLControlException(
					"no keystore, unable to generate certificate.");
		}
		if (new File(serverCertificate).exists()) {
			new File(serverCertificate).delete();
		}

		String[] export = {
				"keytool", "-export", "-keystore", "\"" + keyStoreFile + "\"",
				"-alias", alias, "-file", "\"" + serverCertificate + "\"",
				"-storepass", keyStorePass
		};

		Runtime rt = Runtime.getRuntime();
		String os = System.getProperty("os.name");
		try {
			Process proc = rt.exec(export);
			// if (os.toLowerCase().indexOf("windows") == -1)
			proc.waitFor();
			log.debug("certificate exported to temp file");
		}
		catch (Throwable t) {
			log.debug("unable to export certificate to temp file");
			throw new SSLControlException(t.getMessage(), t);
		}

		/* just in case */
		if (new File(trustStoreFile).exists()) {
			new File(trustStoreFile).delete();
			log.debug("old truststore file deleted: " + trustStoreFile);
		}
		/*
		 * there must be storepass and it must be longer than 5 chars in order
		 * for Runtime to work (suppress any prompt)
		 */
		String importt = "keytool -import -noprompt -keystore " + "\""
				+ trustStoreFile + "\"" + " -alias " + alias + " -file " + "\""
				+ serverCertificate + "\"" + " -storepass " + trustStorePass;

		// System.out.println("\n--- EXEc:\n" + importt + "\n--------");
		try {
			Process proc = rt.exec(importt);
			proc.waitFor();

			// if (os.toLowerCase().indexOf("windows") == -1)

			// Windows seems not to like this
			// rt.exec(removeCmd+" "+tempFile);
			/*
			 * if having problem, switch to File.delete() rm or delete may not
			 * be better
			 */
			new File(serverCertificate).delete();
			log.debug("certificate imported to trustStore");
		}
		catch (Throwable t) {
			log.warn("unable to import certificate to trustStore");
			throw new SSLControlException(t.getMessage(), t);
		}

		return trustStoreFile;
	}
}

// Under /home/lhb/Narada_misc/jsse-source/ there is file generatekeys.sh
